/*
A small program to extract specified index from standard input.

Any number of index may be specified. Fields are numbered starting at one.
Any field may be specified in any order desired. A field may be specified any
number of time. Fields in the specification that do not exist in the input are
skipped. A default value for a nonexistant field may be specified by the -f
switch. To halt if a specified field does not exist use the -s flag.

ENVIRONMENT VARIABLES

Four environment variables control field(1)
		IFS - any rune in IFS delimits a field
			default: space or tab
		OFS - the string that delimits output of the selected a field
			default: space
		RS  - the single byte that delimits lines in the input
			default: newline
		ORS - the string that separates output records.
			default: newline
*/
package main

import (
	"flag"
	"log"
	"os"
	"strconv"
	"fmt"
	"bufio"
	"strings"
)

var (
	strict = flag.Bool("s", false, "fail if no such field")
	filler = flag.String("f", "", "value to use if no such field")
)

func usage(v ...interface{}) {
	if len(v) != 0 {
		log.Println(v...)
	}
	log.Println("Usage: field [flags] fieldno+")
	log.Println("Flags:")
	flag.PrintDefaults()
	os.Exit(1)
}

func envor(name, def string) string {
	e, err := os.Getenverror(name)
	if err != nil {
		return def
	}
	return e
}

func smsg(lno int) {
	log.Fatalln("Not enough index in line", lno)
}

//Usage: %name %flags fieldno+
func main() {
	log.SetFlags(0)

	//get env vars
	IFS, OFS, ORS := envor("IFS", " \t"), envor("OFS", " "), envor("ORS", "\n")
	var RS byte
	if rs := []byte(envor("RS", "\n")); len(rs) != 1 {
		log.Fatalln("$RS must be exactly one byte long")
	} else {
		RS = rs[0]
	}

	//handle command line flags
	flag.Usage = func() { usage() }
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		usage("No index specified")
	}

	//get the requested indicies
	index := make([]int, len(args))
	for i, a := range args {
		f, err := strconv.Atoi(a)
		if err != nil {
			usage(err)
		}
		if f < 1 {
			usage("Not a valid field number")
		}
		index[i] = f - 1
	}

	//need to know what the largest field requested is so we can break easier
	//in strict mode.
	max := -1
	if *strict {
		for _, i := range index {
			if max == -1 || max < i {
				max = i
			}
		}
	}

	//jigger fieldsfunc to use IFS
	ifs := []int(IFS)
	var ifser = func(r int) bool {
		for _, rune := range ifs {
			if r == rune {
				return true
			}
		}
		return false
	}

	//type is []interface{} so we can ... it into fmt.Print
	buffer := make([]interface{}, 2*len(index))

	//main loop
	in := bufio.NewReader(os.Stdin)
	lno := 0
	has_filler := *filler != ""
	for {
		line, err := in.ReadString(RS)
		lno++

		//knock out those pesky edge cases
		switch len(line) {
		case 0: //EOF
			return
		case 1: //just RS
			if *strict {
				smsg(lno)
			}
			fmt.Print(ORS)
			continue
		}

		//break into index
		fields := strings.FieldsFunc(line[:len(line)-1], ifser)

		//strict mode and not enough index
		if *strict && len(fields) < max {
			smsg(lno)
		}

		//gather fields
		var i int
		for _, f := range index {
			if f < len(fields) {
				buffer[i] = fields[f]
			} else if has_filler {
				buffer[i] = *filler
			}

			if f < len(fields) || has_filler {
				i++
				buffer[i] = OFS
				i++
			}
		}
		buffer[i-1] = ORS

		//actually do what we came here for
		fmt.Print(buffer[:i]...)

		//this is the EOF from the ReadString from forever ago 
		if err != nil {
			return
		}
	}
}
